package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.dto.leanring.SignInMessage;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.constants.RedisConstant;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.SignRecord;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mapper.SignRecordMapper;
import com.tianji.learning.service.ISignRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 签到记录表 服务实现类
 * </p>
 *
 * @author xjs
 */
@Service
public class SignRecordServiceImpl extends ServiceImpl<SignRecordMapper, SignRecord> implements ISignRecordService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitMqHelper mqHelper;

    /**
     * 使用redis的bitmap存储用户的签到记录
     * key: yyyyMM:userId  value: bitmap
     * */
    @Override
    public SignResultVO addSignRecords() {
        Long userId = UserContext.getUser();
        // 获取redis中bitmap的key
        String key = buildUserSignRecordKey(userId);
        // 先判断是否签过到了
        int day = LocalDateTime.now().getDayOfMonth();
        int offset = day - 1;
        Boolean ret = redisTemplate.opsForValue().setBit(key, offset, true);
        if(ret != null && ret){
            // 已经签过了 抛异常
            throw new BizIllegalException("不能重复签到");
        }
        // 没签过, do nothing, 因为已经设置好了
        int days = calcContinousDays(userId, day);
        //    计算连续签到的天数,计算奖励 的积分
        int rewardsPoint = 0;
        if(days == 7){
            rewardsPoint = 10;
        }else if(days == 14){
            rewardsPoint = 20;
        }else if(days == 28){
            rewardsPoint = 40;
        }
        // 返回
        SignResultVO vo = new SignResultVO();
        vo.setRewardPoints(rewardsPoint);
        vo.setSignPoints(1);
        vo.setSignDays(days);
        // 增加积分, 发消息
        mqHelper.send(MqConstants.Exchange.LEARNING_EXCHANGE, MqConstants.Key.SIGN_IN, SignInMessage.of(userId, rewardsPoint+1));
        return vo;
    }

    @Override
    public List<Integer> getSignRecords() {
        Long userId = UserContext.getUser();
        int day = LocalDateTime.now().getDayOfMonth();
        // 获取用户签到的bitmap值
        Long value = getUserSignRecordValue(userId, day);
        List<Integer> ret = new ArrayList<>();
        for(int i=0; i<day;i++){
            ret.add(0, (int)(value & 1));
            value = value >>> 1;
        }
        return ret;
    }

    /**
     * 计算连续签到的天数
     * 先取出截至今天位置所有的签到的bit位的整数值
     * 把整数值转化成二进制bit
     * 从后往前数1的个数
     * */
    private int calcContinousDays(Long userId, int day) {
        // 获取用户签到的bitmap值
        Long value = getUserSignRecordValue(userId, day);
        int count = 0;
        // 比较最后一个bit位是否是1
        while ( (value & 1) == 1 ){
            // 无符号右移1个bit位继续比较
            value = value >>> 1;
            count ++;
        }
        return count;
    }

    private Long getUserSignRecordValue(Long userId,int day){
        // 获取redis中的值
        String key = buildUserSignRecordKey(userId);
        // 从redis中取签到的bitmap
        List<Long> longs = redisTemplate.opsForValue().bitField(key,
                // 取day个转化成无符号的整数， 从0开始取
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(day)).valueAt(0));
        if(CollUtils.isEmpty(longs)){
            return 0L;
        }
        return longs.get(0);
    }

    private String buildUserSignRecordKey(Long userId){
        String yyyyMM = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        // 构造bitmap的key
         return RedisConstant.SIGN_PREFIX + yyyyMM +":" + userId;
    }
}
